#pragma once

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <sys/socket.h>
#include <sys/fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>

#include "error.hpp"

class UdpSock
{
public:
    UdpSock() {}
    ~UdpSock() {}

    void Socket()
    {
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            std::cerr << "socket error:" << strerror(errno) << std::endl;
            exit(SOCKET_ERROR);
        }
        std::cout << "socket success" << std::endl;
        // int opt = 1;
        // int ret = setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
        // if (ret < 0)
        // {
        //     std::cerr << "setsockopt error:" << strerror(errno) << std::endl;
        //     exit(SETSOCKOPT_ERROR);
        // }
        // std::cout << "setsockopt success" << std::endl;
    }

    bool Bind(const uint16_t &port, const std::string &ip = "")
    {
        struct sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        if (ip != "")
        {
            addr.sin_addr.s_addr = inet_addr(ip.c_str());
        }
        else
        {
            addr.sin_addr.s_addr = htonl(INADDR_ANY);
        }
        int ret = bind(_sockfd, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr));
        if (ret < 0)
        {
            std::cerr << "bind error:" << strerror(errno) << std::endl;
            return false;
        }
        std::cout << "bind success" << std::endl;
        return true;
    }

    bool Recv(std::string *msg, struct sockaddr_in *peer, socklen_t *len)
    {
        char buf[1024] = {0};
        int n = recvfrom(_sockfd, buf, sizeof(buf) - 1, 0, reinterpret_cast<struct sockaddr *>(peer), len);
        if (n < 0)
        {
            std::cerr << "recvfrom error:" << strerror(errno) << std::endl;
            return false;
        }
        else if (n == 0)
        {
            std::cerr << "peer shutdown" << std::endl;
            return false;
        }
        else
        {
            (*msg) += buf;
            return true;
        }
    }

    bool Send(const std::string &msg, const struct sockaddr_in &peer, const socklen_t &len)
    {
        int n = sendto(_sockfd, msg.c_str(), msg.size(), 0, reinterpret_cast<const struct sockaddr *>(&peer), len);
        if (n < 0)
        {
            std::cerr << "sendto error:" << strerror(errno) << std::endl;
            return false;
        }
        return true;
    }

private:
    int _sockfd;
};